package com.cy.pj.common.aspect;

import com.cy.pj.common.annotation.ClearCache;
import com.cy.pj.common.annotation.RequiredCache;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;

import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

@Aspect
@Component
public class SysModuleCacheAspect {
    private Map<String, Map<String ,Object>> cacheMap = new ConcurrentHashMap<>();

    @Pointcut("@annotation(com.cy.pj.common.annotation.RequiredCache)")
    private void doCache(){}

    @Pointcut("@annotation(com.cy.pj.common.annotation.ClearCache)")
    private void doClearCache(){}

    @AfterReturning("doClearCache()")
    public void doAfterReturning(JoinPoint joinPoint) throws NoSuchMethodException {
        String cacheName = getCacheName(ClearCache.class,joinPoint);
        Map<String ,Object> cache = cacheMap.get("cacheName");
        cache.clear();
    }

    private String getCacheName(Class<? extends Annotation> annoClass, JoinPoint joinPoint) throws NoSuchMethodException {
        Class<?> tagetClass = joinPoint.getTarget().getClass();
        MethodSignature ms = (MethodSignature) joinPoint.getSignature();
        Method targetMethod = tagetClass.getMethod(ms.getName(),ms.getParameterTypes());
        Annotation annotation = targetMethod.getAnnotation(annoClass);
        String cacheName = null;
        if (annotation instanceof RequiredCache) {
            RequiredCache requiredCache = (RequiredCache) targetMethod.getAnnotation(annoClass);
            cacheName = requiredCache.name();
        } else if (annotation instanceof ClearCache) {
            ClearCache clearCache = (ClearCache) targetMethod.getAnnotation(annoClass);
            cacheName = clearCache.name();
        }
        return cacheName;
    }

    @Around("doCache()")
    public Object doCacheAround(ProceedingJoinPoint joinPoint) throws Throwable {
        System.out.println("Get data from cache");
        String cacheName = getCacheName(RequiredCache.class,joinPoint);
        Map<String ,Object> cache = cacheMap.get("cacheName");
        if (cache == null) {
            cache = new ConcurrentHashMap<>();
            cacheMap.put("cacheName",cache);
        }
        Object result = cache.get("methodKey");
        if (result != null) {
            return result;
        }
        result = joinPoint.proceed();
        System.out.println("Put data to cache");
        cache.put("methodKey",result);
        return result;
    }
}
